{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# simple neural network of one complex valued neuron\n",
    "import numpy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class neuralNetwork:\n",
    "    \n",
    "    def __init__(self):\n",
    "        # link weights matrix\n",
    "        self.w = numpy.random.normal(0.0, pow(1.0, -0.5), (3))\n",
    "        self.w = numpy.array(self.w, ndmin=2, dtype='complex128')\n",
    "        self.w += 1j * numpy.random.normal(0.0, pow(1.0, -0.5), (3))\n",
    "        \n",
    "        # testing overrride\n",
    "        #self.w = numpy.array([1.0 + 0.0j, 1.0 + 0.0j], ndmin=2, dtype='complex128')\n",
    "        \n",
    "        # number of output class categories\n",
    "        self.categories = 2\n",
    "        \n",
    "        # todo periodicity self.periodicity = 2\n",
    "        pass\n",
    "    \n",
    "    def z_to_class(self, z):\n",
    "        # first work out the angle, but shift angle from [-pi/2, +pi.2] to [0,2pi]\n",
    "        angle = numpy.mod(numpy.angle(z) + 2*numpy.pi, 2*numpy.pi)\n",
    "        # from angle to category\n",
    "        p = int(numpy.floor (self.categories * angle / (2*numpy.pi)))\n",
    "        return p\n",
    "\n",
    "    def class_to_angle(self, p):\n",
    "        # class to angle, using bisector\n",
    "        angle = ((p + 0.5) / self.categories) * 2 * numpy.pi\n",
    "        return angle\n",
    "    \n",
    "    def status(self):\n",
    "        print (\"w = \", self.w)\n",
    "        pass\n",
    "\n",
    "    def query(self, inputs_list):\n",
    "        # add bias input\n",
    "        inputs_list.append(1.0)\n",
    "        \n",
    "        # convert input to complex\n",
    "        inputs = numpy.array(inputs_list, ndmin=2, dtype='complex128').T\n",
    "        print(\"inputs = \\n\", inputs)\n",
    "        \n",
    "        # combine inputs, weighted\n",
    "        z = numpy.dot(self.w, inputs)\n",
    "        print(\"z = \", z)\n",
    "        \n",
    "        # map to output classes\n",
    "        o = self.z_to_class(z)\n",
    "        print(\"output = \", o)\n",
    "        print (\"\")\n",
    "        return o\n",
    "    \n",
    "    def train(self, inputs_list, targets_list):\n",
    "        # add bias input\n",
    "        inputs_list.append(1.0)\n",
    "        \n",
    "        # convert inputs and outputs list to 2d array\n",
    "        inputs = numpy.array(inputs_list, ndmin=2, dtype='complex128').T\n",
    "        targets = numpy.array(targets_list, ndmin=2).T\n",
    "\n",
    "        # combine inputs, weighted\n",
    "        z = numpy.dot(self.w, inputs)\n",
    "        #print(\"z = \", z)\n",
    "        \n",
    "        # desired angle from trainging set\n",
    "        desired_angle = self.class_to_angle(targets)\n",
    "        #print(\"desired_angle = \", desired_angle)\n",
    "        \n",
    "        # error e\n",
    "        e =  numpy.exp(1j*desired_angle) - z\n",
    "        #print(\"e = \", e)\n",
    "        \n",
    "        # dw = e * x.T / (x.x.T)\n",
    "        dw = (e * numpy.conj(inputs.T)) / (3)\n",
    "        #print(\"dw = \", dw)\n",
    "        self.w += dw\n",
    "        #print(\"new self.w = \", self.w )\n",
    "        #print(\"test new self.w with query = \", self.query(inputs.T))\n",
    "        #print(\"--\")\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "w =  [[-1.06236919+0.04312478j  0.50718034+1.69705651j -0.81702728-0.51196168j]]\n"
     ]
    }
   ],
   "source": [
    "# create instance of neural network\n",
    "n = neuralNetwork()\n",
    "n.status()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# train neural network - OR\n",
    "for i in range(5):\n",
    "    n.train([-1.0, -1.0], [0])\n",
    "    n.train([-1.0, 1.0], [1])\n",
    "    n.train([1.0, -1.0], [1])\n",
    "    n.train([1.0, 1.0], [1])\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "w =  [[ -6.35598582e-06-0.33336145j   3.17265351e-05-0.16667827j\n",
      "   -2.53705493e-05-0.49996028j]]\n",
      "inputs = \n",
      " [[-1.+0.j]\n",
      " [-1.+0.j]\n",
      " [ 1.+0.j]]\n",
      "z =  [[ -5.07410985e-05 +7.94330603e-05j]]\n",
      "output =  0\n",
      "\n",
      "inputs = \n",
      " [[-1.+0.j]\n",
      " [ 1.+0.j]\n",
      " [ 1.+0.j]]\n",
      "z =  [[  1.27119716e-05-0.3332771j]]\n",
      "output =  1\n",
      "\n",
      "inputs = \n",
      " [[ 1.+0.j]\n",
      " [-1.+0.j]\n",
      " [ 1.+0.j]]\n",
      "z =  [[ -6.34530701e-05-0.66664346j]]\n",
      "output =  1\n",
      "\n",
      "inputs = \n",
      " [[ 1.+0.j]\n",
      " [ 1.+0.j]\n",
      " [ 1.+0.j]]\n",
      "z =  [[ -1.83663848e-16-1.j]]\n",
      "output =  1\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# query after training\n",
    "n.status()\n",
    "n.query( [-1.0, -1.0] )\n",
    "n.query( [-1.0, 1.0] )\n",
    "n.query( [1.0, -1.0] )\n",
    "n.query( [1.0, 1.0] )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
